import { cloneElement, CSSProperties, ReactElement, useEffect, useRef, useState } from 'react'
import classNames from 'classnames'
import { Image as AntImage, ImageProps } from 'antd'

import { fileValidate } from '@/utils/utils'
import _defaultImg from '@/assets/defaultImg.svg'
import _errImg from '@/assets/errImg.svg'
import styles from './styles.module.less'

const defaultImg = _defaultImg + '#defaultImg'
const errImg = _errImg + '#errImg'

interface IImgProps extends ImageProps {
  height: string | number
  width: string | number
  src: string
  /**
   * 缩略图按长边缩放（仅图片有效；不要随意变更缩放值，尽可能保持稳定）
   * @default 100
   */
  ossResizeL?: number
}

/** antd Image 封装 */
export const CusImage = Object.assign(
  ({ height, width, src, ossResizeL, preview, placeholder, ...rest }: IImgProps) => {
    const loadingDelay = useRef(randArrItem([0.4, 0.5, 0.6])).current
    const [isErr, setIsErr] = useState(false)

    let newSrc = src

    // 不可预览
    if (!src || src === CusImage.defaultImg || src === CusImage.errImg || isErr) {
      preview = false
    }
    // OSS
    else if (src.includes('.aliyuncs.com/') && !/\?|#/.test(src) && fileValidate.isImgUrl(src)) {
      newSrc = `${src}?x-oss-process=image/resize,l_${Math.max(Number(ossResizeL), 0) || 100}`
      if (preview === undefined || preview) {
        preview = {
          src: `${src}?x-oss-process=image/resize,w_${1000}`,
          imageRender: (originalNode, info) => <PreviewPlaceholder url={info.image.url} originalNode={originalNode} />,
          ...Object(preview),
        }
      }
    }

    return (
      <AntImage
        {...rest}
        wrapperClassName={classNames(rest.wrapperClassName, styles.wrap)}
        className={classNames(rest.className, styles.img)}
        placeholder={placeholder || getPlaceholder(loadingDelay)}
        src={newSrc}
        preview={preview}
        fallback={rest.fallback || CusImage.errImg}
        onError={e => {
          setIsErr(true)
          rest.onError?.call(null, e)
        }}
        wrapperStyle={{
          ...rest.wrapperStyle,
          height,
          width,
        }}
        style={{
          ...rest.style,
          maxHeight: height,
          maxWidth: width,
        }}
      />
    )
  },

  {
    /** 默认图 */
    defaultImg: defaultImg,
    /** 异常图 */
    errImg: errImg,
  },
)

function getPlaceholder(loadingDelay: number) {
  return (
    <div className={styles.placeholder}>
      <img src={CusImage.defaultImg} style={{ '--loading-delay': `${loadingDelay}s` } as any} />
    </div>
  )
}

function PreviewPlaceholder({ url, originalNode }: { url: string; originalNode: ReactElement }) {
  const [loading, setLoading] = useState(true)
  const [isErr, setIsErr] = useState(false)

  useEffect(() => {
    const img = new Image()
    setLoading(true)
    setIsErr(false)
    img.onload = () => void setLoading(false)
    img.onabort = () => {
      setLoading(false)
      setIsErr(true)
    }
    img.onerror = () => {
      setLoading(false)
      setIsErr(true)
    }
    img.src = url
    return () => {
      img.onload = null
      img.onabort = null
      img.onerror = null
      img.src = ''
    }
  }, [url])

  if (!loading && !isErr) return originalNode

  return (
    <>
      {cloneElement<{ style?: CSSProperties }>(originalNode, {
        style: {
          ...originalNode.props?.style,
          display: loading || isErr ? 'none' : originalNode.props?.style?.display,
        },
      })}
      {(loading || isErr) && (
        <img
          className={classNames(styles.previewPlaceholderImg, loading && styles.isPreviewLoading)}
          src={loading ? CusImage.defaultImg : CusImage.errImg}
        />
      )}
    </>
  )
}

function randArrItem<T>(arr: readonly T[]): T {
  return arr[Math.floor(Math.random() * arr.length)]
}
